package com.gofaraway.service.student.base;

import cn.hutool.core.collection.CollUtil;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author ChenPeng
 * @Date 2024/8/19 17:33
 */
public class LocalCacheAndConcurrent {


    @Data
    public class ShopInfo {
        //商户Id
        private Integer id;

        //商户名称
        private String name;

        //商户地址
        private String address;

    }


    @Service
    public class ShopService {

        //已经提供的方法1:批量根据商户id查询商户名称
        //限制:入参shopIds不大于10个
        public Map<Integer, String> getShopName(List<Integer> shopIds) {
            return Collections.emptyMap();
        }

        //已经提供的方法2:批量根据商户id查询商户地址
        //限制:入参shopIds不大于10个
        public Map<Integer, String> getShopAddress(List<Integer> shopIds) {
            return Collections.emptyMap();
        }
    }

    public class AService {

        @Resource
        private ShopService shopService;

        private final ConcurrentHashMap<Integer, ShopInfo> CACHE_MAP = new ConcurrentHashMap<>(20);
        private final ExecutorService threadPool = Executors.newScheduledThreadPool(10);

        /*
         目标:根据商户id批量查询商户信息,入参shopIds不大于100个
         要求:
         1.并行调用ShopService提供的getShopName和getShopAddress方法获取商户名称和地址,构造ShopInfo
         2.从ShopService获取数据时,需要满足ShopService"入参shopIds不大于10个"的限制条件,即需要分批调用
         3.实现本地缓存,优先从本地缓存获取数据,如果本地缓存没有数据,再从ShopService获取数据
        */
        public Map<Integer, ShopInfo> getShopInfo(List<Integer> shopIds) {
            if (CollectionUtils.isEmpty(shopIds) || shopIds.size() > 100) {
                throw new RuntimeException();
            }

            Map<Integer, ShopInfo> fromCache = shopIds.stream().map(CACHE_MAP::get).filter(Objects::nonNull)
                    .collect(Collectors.toMap(ShopInfo::getId, a -> a, (k1, k2) -> k1));

            if (MapUtils.isNotEmpty(fromCache)) {
                shopIds.removeIf(fromCache::containsKey);
            }

            if (CollectionUtils.isEmpty(shopIds)) {
                return fromCache;
            }

            List<List<Integer>> group = CollUtil.split(shopIds, 10);

            CompletableFuture<Map<Integer, String>> futureNames = CompletableFuture.supplyAsync(() -> group.parallelStream()
                    .map(shopService::getShopName)
                    .flatMap(map -> map.entrySet().stream())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), threadPool);

            CompletableFuture<Map<Integer, String>> futureAddresses = CompletableFuture.supplyAsync(() -> group.parallelStream()
                    .map(shopService::getShopAddress)
                    .flatMap(map -> map.entrySet().stream())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), threadPool);

            CompletableFuture.allOf(futureNames, futureAddresses).join();
            Map<Integer, String> nameMap = futureNames.join();
            Map<Integer, String> addressesMap = futureAddresses.join();

            threadPool.shutdown();

            Map<Integer, ShopInfo> fromService = shopIds.stream()
                    .map(i -> {
                        ShopInfo shopInfo = new ShopInfo();
                        shopInfo.setId(i);
                        shopInfo.setName(nameMap.get(i));
                        shopInfo.setAddress(addressesMap.get(i));
                        return shopInfo;
                    })
                    .collect(Collectors.toMap(ShopInfo::getId, a -> a, (k1, k2) -> k1));

            //放入缓存
            CACHE_MAP.putAll(fromService);

            Map<Integer, ShopInfo> result = new HashMap<>(shopIds.size());
            result.putAll(fromService);
            result.putAll(fromCache);
            return result;
        }
    }


    /**
     * 计算商品的优惠标签
     *
     * @param originalPrice       原价（未扣除优惠前的价格）
     * @param finalPrice          到手价（扣除所有优惠后的价格，即用户需要实际支付的金额）
     * @param historicalPriceList 历史到手价列表（每月一个，0是最近一个月）
     * @return 优惠标签
     */
    public static String computeProductPromoTag(BigDecimal originalPrice, BigDecimal finalPrice, List<BigDecimal> historicalPriceList) {

        if (originalPrice == null || finalPrice == null || CollectionUtils.isEmpty(historicalPriceList)) {
            return "到手价";
        }

        //如果是历史最低，则输出："历史最低"
        boolean isHistoricalLowest = historicalPriceList.stream().allMatch(price -> finalPrice.compareTo(price) <= 0);
        if (isHistoricalLowest) {
            return "历史最低";
        }

        //如果是近3个月最低，则输出："近3月最低"
        List<BigDecimal> lastThreeMonthsPrices = historicalPriceList.subList(0, Math.min(3, historicalPriceList.size()));
        boolean isLastThreeMonthsLowest = lastThreeMonthsPrices.stream().allMatch(price -> finalPrice.compareTo(price) <= 0);
        if (isLastThreeMonthsLowest) {
            return "近3月最低";
        }

        //如果有折扣（折扣要大于0.1且小于9.9，小数点向下取），则输出："xx折"，小数点最多保留1位（向下取），如最后一位小数是0则删除
        if (BigDecimal.ZERO.compareTo(originalPrice) == 0) {
            return "到手价";
        }
        BigDecimal discountPercentage = (originalPrice.subtract(finalPrice).divide(originalPrice, 2, RoundingMode.DOWN)).multiply(BigDecimal.valueOf(10));
        if (discountPercentage.compareTo(BigDecimal.ZERO) > 0 && discountPercentage.compareTo(BigDecimal.valueOf(9.9)) < 0) {
            //格式化折扣文案
            String discountStr = discountPercentage.stripTrailingZeros().toPlainString();
            if (discountStr.endsWith(".0")) {
                discountStr = discountStr.substring(0, discountStr.length() - 2);
            }
            return discountStr + "折";
        }

        return "到手价";
    }

}
